/*
-----------------------------------------------------------------------------
This source file is part of Labor3D
(Labor 3D Graphics Engine)

Copyright (c) 2009-2020 Rock Mao
* creator : Rock Mao
* created : 2009-5-2   22:29
* filename: LaborFrustum.cpp
-----------------------------------------------------------------------------
*/
#include "stdafx.h"

#include "LaborFrustum.h"
#include "../LaborCore.h"
#include "LaborRenderSystem.h"
NAMESPACE_LABOR3D_BEGIN
//---------------------------------------------------------------------
CLFrustum::CLFrustum()
{
	m_matrixProjection.identity();
	m_matrixView.identity();
	m_matrixInvView.identity();
	m_matrixInvProjection.identity();
	m_matrixViewProjection.identity();
	m_matrixInvViewProjection.identity();
}

//---------------------------------------------------------------------
CLFrustum::~CLFrustum()
{
	
}

//-------------------------------------------------------------------------------------
bool CLFrustum::isVisiable( const CLPoint3 &bound, eLFrustumPlane *cullBy/* = NULL */ ) 
{
	// Make any pending updates to the calculated frustum
	_updateView();

	// For each plane, see if all points are on the negative side
	// If so, object is not visible
	for (int plane = 0; plane < 6; ++plane)
	{
		// Skip far plane if infinite view frustum
		if ( m_fZFar == 0 && plane == FrustumPlane_Far )
			continue;

		if ( m_frustumPlanes[plane].getSide(bound) == CLPlane::POSITIVE_SIDE)
		{
			// ALL corners on negative side therefore out of view
			if (cullBy)
				*cullBy = (eLFrustumPlane)plane;
			return false;
		}

	}

	return true;
}

//-------------------------------------------------------------------------------------
bool CLFrustum::isVisiable( const CLSphere &bound, eLFrustumPlane *cullBy /* = NULL */ ) 
{
	// Make any pending updates to the calculated frustum
	_updateView();

	// For each plane, see if sphere is on negative side
	// If so, object is not visible
	for (int plane = 0; plane < 6; ++plane)
	{
		// Skip far plane if infinite view frustum
		if ( m_fZFar == 0 && plane == FrustumPlane_Top )
			continue;

		// If the distance from sphere center to plane is negative, and 'more negative' 
		// than the radius of the sphere, sphere is outside frustum
		if ( m_frustumPlanes[plane].distance(bound.getCenter()) < -bound.getRadius() )
		{
			// ALL corners on negative side therefore out of view
			if (cullBy)
				*cullBy = (eLFrustumPlane)plane;
			return false;
		}

	}

	return true;}

//-------------------------------------------------------------------------------------
bool CLFrustum::isVisiable( const CLAABB &bound, eLFrustumPlane *cullBy /* = NULL */) 
{
	// Null boxes always invisible
	if ( !bound.isValid()) return false;

	// Make any pending updates to the calculated frustum
	_updateView();

	// Get corners of the box
	CLPoint3 pCorners[8];
	bound.computePoints( pCorners );


	// For each plane, see if all points are on the negative side
	// If so, object is not visible
	for (int plane = 0; plane < 6; ++plane)
	{
		// Skip far plane if infinite view frustum
		if (m_fZFar == 0 && plane == FrustumPlane_Far )
			continue;

		if (m_frustumPlanes[plane].getSide(pCorners[0]) == CLPlane::POSITIVE_SIDE &&
			m_frustumPlanes[plane].getSide(pCorners[1]) == CLPlane::POSITIVE_SIDE &&
			m_frustumPlanes[plane].getSide(pCorners[2]) == CLPlane::POSITIVE_SIDE &&
			m_frustumPlanes[plane].getSide(pCorners[3]) == CLPlane::POSITIVE_SIDE &&
			m_frustumPlanes[plane].getSide(pCorners[4]) == CLPlane::POSITIVE_SIDE &&
			m_frustumPlanes[plane].getSide(pCorners[5]) == CLPlane::POSITIVE_SIDE &&
			m_frustumPlanes[plane].getSide(pCorners[6]) == CLPlane::POSITIVE_SIDE &&
			m_frustumPlanes[plane].getSide(pCorners[7]) == CLPlane::POSITIVE_SIDE)
		{
			// ALL corners on negative side therefore out of view
			if (cullBy)
				*cullBy = (eLFrustumPlane)plane;
			return false;
		}

	}

	return true;
}

//-------------------------------------------------------------------------------------
void CLFrustum::enableReflection( CLPlane &plane )
{
	m_bReflectEnabled = true;
	m_planReflect = plane;
	m_matrixReflect.buildReflectionMatrix(plane);

	_viewChanged();
}

//-------------------------------------------------------------------------------------
void CLFrustum::disableReflection()
{
	m_bReflectEnabled = true;

	_viewChanged();
}

//-------------------------------------------------------------------------------------
void CLFrustum::_updateFrustum(void)
{
	if ( m_bFrustumChanged )
	{
		// Common calcs
		float thetaY = m_fFOVy * 0.5f;
		float tanThetaY = tanf(thetaY);
		float tanThetaX = tanThetaY * m_fAspect;
		float vpTop = tanThetaY * m_fZNear;
		float vpRight = tanThetaX * m_fZNear;
		float vpBottom = -vpTop;
		float vpLeft = -vpRight;
		float fNSqr = m_fZNear * m_fZNear;
		float fLSqr = vpRight * vpRight;
		float fRSqr = fLSqr;
		float fTSqr = vpTop * vpTop;
		float fBSqr = fTSqr;
		float fInvLength = 1.0f / fastSqrt( fNSqr + fLSqr );

		// Recalc if frustum params changed
		if (mProjType == ProjectionType_PERSPECTIVE)
		{

			// PERSPECTIVE transform, API specific
			//ILRenderSystem *pCurRenderer = NULL;
			//pCurRenderer = GetLaborCore()->getRenderSystem();
			//if ( pCurRenderer )
			//{
			//	pCurRenderer->makeProjectionMatrix(m_fFOVy, m_fAspect, m_fZNear, m_fZFar, m_matrixProjection);
			//}

			Matrix4x4PerspectiveFovLH(m_matrixProjection, m_fFOVy, m_fAspect, m_fZNear, m_fZFar);
			// Calculate co-efficients for the frustum planes
			// Special-cased for L = -R and B = -T i.e. viewport centered 
			// on direction vector.
			// Taken from ideas in WildMagic 0.2 http://www.magic-software.com
			m_fCoeffL[0] = m_fZNear * fInvLength;
			m_fCoeffL[1] = -vpLeft * fInvLength;

			fInvLength = 1.0f / fastSqrt( fNSqr + fRSqr );
			m_fCoeffR[0] = -m_fZNear * fInvLength;
			m_fCoeffR[1] = vpRight * fInvLength;

			fInvLength = 1.0f / fastSqrt( fNSqr + fBSqr );
			m_fCoeffB[0] = m_fZNear * fInvLength;
			m_fCoeffB[1] = -vpBottom * fInvLength;

			fInvLength = 1.0f / fastSqrt( fNSqr + fTSqr );
			m_fCoeffT[0] = -m_fZNear * fInvLength;
			m_fCoeffT[1] = vpTop * fInvLength;

			m_matrixInvProjection = m_matrixProjection;
			m_matrixInvView.invert();
			m_matrixViewProjection = m_matrixView*m_matrixProjection;
			m_matrixInvViewProjection = m_matrixViewProjection;
			m_matrixInvViewProjection.invert();
		}
		else if (mProjType == ProjectionType_ORTHOGRAPHIC)
		{
		}
	}

	m_bFrustumChanged = false;
}

//-------------------------------------------------------------------------------------
void CLFrustum::_updateView(void)
{

}

//-------------------------------------------------------------------------------------
const CLMatrix4x4 &CLFrustum::getViewMatrix()
{
	_updateView();

	return m_matrixView;
}

//-------------------------------------------------------------------------------------
const CLMatrix4x4 &CLFrustum::getProjectionMatrix()
{
	_updateFrustum();
	_updateView();

	return m_matrixProjection;
}

//-------------------------------------------------------------------------------------
const CLMatrix4x4 &CLFrustum::getViewProjectionMatrix()
{
	_updateFrustum();
	_updateView();

	return m_matrixViewProjection;
}

const CLMatrix4x4 &CLFrustum::getViewProjectionMatrixInverse()
{
	_updateFrustum();
	_updateView();

	return m_matrixInvViewProjection;
}


//-------------------------------------------------------------------------------------
const CLMatrix4x4 &CLFrustum::getViewMatrixInverse()
{
	_updateView();

	return m_matrixInvView;
}

//-------------------------------------------------------------------------------------
const CLMatrix4x4 &CLFrustum::getProjectionMatrixInverse()
{
	_updateFrustum();
	_updateView();

	return m_matrixInvProjection;
}

NAMESPACE_LABOR3D_END